package com.googlecode.delivery.config.impl;

import java.io.BufferedReader;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStreamReader;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.Iterator;
import java.util.Map.Entry;

import org.apache.log4j.Logger;

import com.googlecode.delivery.DeliveryException;
import com.googlecode.delivery.core.impl.DeploymentUnitFactory;
import com.googlecode.delivery.core.impl.EnvironmentHelper;
import com.googlecode.delivery.core.impl.FileUtils;
import com.googlecode.delivery.model.DeploymentUnit;
import com.googlecode.delivery.model.Environment;

/**
 * 
 * @author Stephen Kay
 */
public class ConfigurationHelper {
	private static final Logger LOGGER = Logger
			.getLogger(ConfigurationHelper.class);

	/**
	 * @param sourcePath
	 * @return
	 * @throws DeliveryException
	 */
	public static DeploymentUnit create(File zipFile) throws DeliveryException {
		return DeploymentUnitFactory.create(zipFile);
	}

	/**
	 * @param root
	 * @param environment
	 * @param outputDir
	 * @return
	 * @throws IOException
	 * @throws DeliveryException
	 */
	public static File configure(DeploymentUnit root, Environment environment,
			File outputDir) throws IOException, DeliveryException {

		// replaces environment file in deployment unit
		if (environment != null) {
			check(root, environment);
			FileUtils.copyFile(environment.getEnvFile(), root.getEnvFile());
			root.setEnvironment(environment);
		}
		override(root,root.getConfigDir());
		
		File outputZip = new File(outputDir + "/" + root.getArtefactName());
		// zips the deployment unit to the output dir.
		FileUtils.zip(root.getExplodedDir(), outputZip);
		LOGGER.info("Configured artifact released: "
				+ outputDir.getAbsolutePath());

		return outputDir;
	}

	/**
	 * @param du
	 * @param env
	 * @throws DeliveryException
	 * @throws IOException
	 */
	static void check(DeploymentUnit du, Environment env)
			throws DeliveryException {
		Environment duEnv = du.getEnvironment();

		// checks environment compatibility
		String msgError = "";
		ArrayList<String> undefinedKeys = new ArrayList<String>();
		for (Enumeration<?> keys = duEnv.keys(); keys.hasMoreElements();) {
			String key = (String) keys.nextElement();
			if (env.getProperty(key) == null) {
				undefinedKeys.add(key);
				msgError += key + ",";
			}
		}
		if (undefinedKeys.size() > 0) {
			throw new DeliveryException("Environment " + env.getName()
					+ " not compatible - keys not found :" + msgError);
		}
	}

	/**
	 * @param du
	 * @param cfgFile
	 * @throws IOException
	 */
	static void override(DeploymentUnit du, File cfgFile) throws IOException {
		if (cfgFile.isDirectory()) {
			File[] files = cfgFile.listFiles();
			for (int i = 0; i < files.length; i++) {
				override(du, files[i]);
			}
			return;
		}
		// otherwise, this is a config file.
		BufferedReader in = new BufferedReader(new InputStreamReader(
				new FileInputStream(cfgFile)));
		StringBuffer sb = new StringBuffer();
		try {
			// iterates over the file lines.
			String line = null;
			while ((line = in.readLine()) != null) {
				sb.append(line).append(System.getProperty("line.separator"));
			}
		} finally {
			in.close();
		}
		// Filtering contents with environment values
		sb = doFiler(sb, du.getEnvironment());

		String cfgRelativePath = cfgFile.getAbsolutePath().substring(
				du.getConfigDir().getAbsolutePath().length());
		// writing results
		File outCfg = new File(du.getExplodedDir().getAbsolutePath()
				+ cfgRelativePath);
		LOGGER.debug("Writing configuration :" + outCfg);
		FileOutputStream fos = new FileOutputStream(outCfg);
		try {
			fos.write(sb.toString().getBytes());
		} finally {
			fos.close();
		}
	}

	/**
	 * Filters the contents with given environment file.
	 * 
	 * @param cfin
	 * @param environment
	 * @return
	 * @throws DeliveryException
	 */
	static StringBuffer doFiler(StringBuffer contents, Environment environment) {
		String out = contents.toString();
		// replaces contents with EACH environemet property values
		for (Iterator<Entry<Object, Object>> iterator = environment.entrySet()
				.iterator(); iterator.hasNext();) {
			Entry<Object, Object> property = iterator.next();
			String token = EnvironmentHelper.getTokenizedKey((String) property
					.getKey());
			// line = line.replaceAll(token, (String) property.getValue());
			out = out.replaceAll(token, (String) property.getValue());
		}
		StringBuffer sb = new StringBuffer(out);
		return sb;
	}
}
